package socketmvc.client.ws;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import socketmvc.client.context.SocketChannel;
import socketmvc.client.context.ThreadSocketChannelHandler;
import socketmvc.client.packets.SocketConnectedPacket;
import socketmvc.client.session.SocketCliSession;
import socketmvc.core.adapter.SocketComponentAdapter;
import socketmvc.core.config.SocketMvcConfig;
import socketmvc.core.context.ConfigContext;
import socketmvc.core.exception.ResponseException;
import socketmvc.core.exception.SocketNotConnectedException;
import socketmvc.core.filter.AntPathFilterChain;
import socketmvc.core.packets.ErrorPacket;
import socketmvc.core.packets.SocketClosePacket;
import socketmvc.core.packets.basic.IPacket;
import socketmvc.core.util.PacketHandler;
import java.nio.ByteBuffer;
import java.util.concurrent.locks.LockSupport;

public class WsMsgHandler {

    private static final Logger logger = LoggerFactory.getLogger(WsMsgHandler.class);
    private final SocketComponentAdapter componentAdapter;
    private final PacketHandler packetHandler;
    private final SocketMvcConfig mvcConfig;
    private SocketCliSession session;
    private volatile boolean reconnecting = false; // 标识是否正在重试连接


    public WsMsgHandler(SocketMvcConfig socketMvcConfig) {
        this.mvcConfig = socketMvcConfig;
        this.componentAdapter = new SocketComponentAdapter(
                socketMvcConfig.getProcessorMethodHandler(),
                socketMvcConfig.getProtocolCodecHandler(),
                socketMvcConfig.getFilterHandler());
        this.packetHandler = new PacketHandler(this.componentAdapter);
        ConfigContext.setConfig(this.mvcConfig);
    }

    public PacketHandler getPacketHandler() {
        return packetHandler;
    }

    public SocketCliSession getSession() {
        return session;
    }

    public void setSession(SocketCliSession session) {
        this.session = session;
    }


    public void onOpen(Short httpStatus, String statusMessage) {
        reconnecting = false;
        String printStr = """
                
                --------------------------------------------------------------------------------------------------------
                    socketmvc client connect successfully! server host is %s , listen on %s.
                --------------------------------------------------------------------------------------------------------
                """.formatted(this.mvcConfig.getIpConfig().getHost(),this.session.getPort());
        System.out.println(printStr);
        SocketChannel context = new SocketChannel(session, packetHandler);
        SocketConnectedPacket packet = new SocketConnectedPacket(Math.toIntExact(httpStatus),statusMessage);
        context.setPacket(packet);
        context.setServerIpConfig(mvcConfig.getIpConfig());
        ThreadSocketChannelHandler.set(context);

        AntPathFilterChain filterChain = new AntPathFilterChain(componentAdapter.getFilters(context,packet));
        try {
            filterChain.doFilter(context, null);
        }catch (Exception e) {
            printErr("An exception occurred in the filter chain ===>", e);
        } finally {
            ThreadSocketChannelHandler.remove();
        }
    }


    public void onMessage(String message) {
        SocketChannel context = new SocketChannel(session, packetHandler);
        SocketClosePacket packet = new SocketClosePacket(-99,"暂不支持text类型消息");
        context.send(packet);
    }


    public void onMessage(ByteBuffer bytes) {
        IPacket imPacket = packetHandler.toPacket(bytes);
        SocketChannel context = new SocketChannel(session, packetHandler);
        context.setPacket(imPacket);
        context.setServerIpConfig(mvcConfig.getIpConfig());
        ThreadSocketChannelHandler.set(context);
        // 构造过滤器器链条，并开始执行链条内的任务
        AntPathFilterChain filterChain = new AntPathFilterChain(componentAdapter.getFilters(context,imPacket));
        try {
            filterChain.doFilter(context, null);
        }catch (ResponseException re){
            printErr("An exception occurred in the filter chain ===>", re);
            context.send(new ErrorPacket(re.getResponseMsg(),imPacket.getResponseKey()));
        } catch (Exception e) {
            printErr("An exception occurred in the filter chain ===>", e);
            context.send(new ErrorPacket(e,imPacket));
        } finally {
            ThreadSocketChannelHandler.remove();
        }
    }


    public void onClose(int code, String reason, boolean remote) {
        if(code < 0 || code > 1004){
            throw new SocketNotConnectedException("websocket意外断开: " + code + " - " + reason);
        }
        SocketChannel context = new SocketChannel(session, packetHandler);
        SocketClosePacket packet = new SocketClosePacket(code,reason);
        context.setPacket(packet);
        context.setServerIpConfig(mvcConfig.getIpConfig());
        ThreadSocketChannelHandler.set(context);

        // 构造过滤器器链条，并开始执行链条内的任务
        AntPathFilterChain filterChain = new AntPathFilterChain(componentAdapter.getFilters(context,packet));
        try {
            filterChain.doFilter(context, null);
        }catch (Exception e) {
            printErr("An exception occurred in the filter chain ===>", e);
        } finally {
            ThreadSocketChannelHandler.remove();
        }
    }


    public void onError(Exception ex) {
        // 错误处理
        printErr("socket 连接发生异常===> ",ex);
        if (ex instanceof SocketNotConnectedException){
            if(session.isConnected()){
                // 已连接成功
                return;
            }
            if(reconnecting){
                // 正在连接的情况排除,保证只有一个线程在同一时间去尝试连接服务器
                return;
            }
            synchronized (WsMsgHandler.class){
                if(session.isConnected()){
                    // 已连接成功
                    return;
                }
                if(reconnecting){
                    // 正在连接的情况排除,保证只有一个线程在同一时间去尝试连接服务器
                    return;
                }
                // 等待3s后再次尝试重连
                reconnecting = true;
                LockSupport.parkNanos(2000L * 1000000);
                session.reconnect();
                reconnecting = false;
            }
        }
    }

    /**
     * 打印异常
     *
     * @param msg 异常信息
     * @param e   ex
     */
    private void printErr(String msg, Exception e) {
        if (logger.isErrorEnabled()) {
            logger.error(msg, e);
        } else {
            System.err.println(msg);
            e.printStackTrace(System.err);
        }
    }
}
